package Server;

import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import Application.WorkUnit;
import Application.WorkUnitImplementation;

public class ClientDispatcher {
	private HashMap<Integer, ClientHandler> clientIdToHandler = new HashMap<Integer, ClientHandler>();
	private HashMap<Integer, List<ClientHandler>> workUnitIdToClients = new HashMap<Integer, List<ClientHandler>>();
	private static Random rand = new Random();
	private static int nextClientID = 1;
	private static int nextWorkUnitID = 1;

	public void addNewClient(Socket socket) {
		new ClientHandler(socket, this);
	}

	// TODO [ClientDispatcher:gotNewClient()] Add Tests
	public int gotNewClient(int clientID, ClientHandler clientHandler) {
		if (clientID == 0) {
			clientID = getNewClientID();
			clientIdToHandler.put(clientID, clientHandler);
		}

		return clientID;
	}

	public void sendUnit(WorkUnit unit) {

	}

	public int getConnectionCount() {
		int count = 0;
		for (Integer index : clientIdToHandler.keySet()) {
			ClientHandler clientHandler = clientIdToHandler.get(index);
			if (clientHandler.isAlive()) {
				count++;
			}
		}
		return count;
	}

	public void sendUnit(int number) {
		System.out.println("Sending " + number + " units...");
		if (getConnectionCount() > 0) {
			int clientCount = getConnectionCount();
			int unitsPerClient = number / clientCount;
			int remainder = number % clientCount;

			List<Integer> inputs = getIntRange(1, 1000);
			WorkUnit unit = new WorkUnitImplementation(0, 0);

			int inputsPerClient = inputs.size() / clientCount;
			int i = 0;
			for (ClientHandler clientHandler : clientIdToHandler.values()) {
//				for (int j = 0; j < unitsPerClient; j++) {
					// clientConnections.get(i).sendUnit("blasdf");
					unit.setInputs(inputs.subList(i * (inputsPerClient),
							(i + 1) * inputsPerClient + 1));
					clientHandler.sendUnit(unit);
					System.out.println("Sent unit.");
//				}
			// if (remainder-- > 0) {
			// clientConnections.get(i).sendUnit("overflow");
			// }
			i++;}
		} else {
			System.err.println("Error: There are no clients connected");
		}
	}
	
	public void finishedWorkUnit(WorkUnit workUnit, ClientHandler clientHandler) {
		System.out.println("[ClientDispatcher] Recieved compelted workUnit.");
		for (Object object : workUnit.getOutputs()) {
			System.out.println(object);
		}
	}

	public List<Integer> getClientIDs() {
		List<Integer> result = new LinkedList<>();
		for (Integer integer : clientIdToHandler.keySet().toArray(
				new Integer[] {}))
			result.add(integer);
		return result;
	}

	public List<ClientHandler> getClientConnections() {
		List<ClientHandler> result = new LinkedList<>();
		for (ClientHandler clientHandler : clientIdToHandler.values().toArray(
				new ClientHandler[] {}))
			result.add(clientHandler);
		return result;
	}

	private List<Integer> getIntRange(int start, int end, int step) {
		List<Integer> result = new LinkedList<Integer>();

		for (int i = start; i < end; i += step)
			result.add(i);

		return result;
	}

	private List<Integer> getIntRange(int start, int end) {
		return getIntRange(start, end, 1);
	}

	private List<Integer> getIntRandom(int size) {
		List<Integer> result = new LinkedList<Integer>();

		for (int i = 0; i < size; i++)
			result.add(rand.nextInt());

		return result;
	}

	private int getNewClientID() {
		return nextClientID++;
	}

	private int getNewWorkUnitID() {
		return nextWorkUnitID++;
	}
}

// TODO Handle clients coming back